﻿// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Collections.Immutable;

using DiscoveryAndExecutionTests.Utilities;

using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;

using TestResult = Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult;

namespace Microsoft.MSTestV2.CLIAutomation;

public abstract partial class CLITestBase
{
    internal static ImmutableArray<TestCase> DiscoverTests(string assemblyPath, string? testCaseFilter = null)
    {
        var unitTestDiscoverer = new UnitTestDiscoverer(new TestSourceHandler());
        var logger = new InternalLogger();
        var sink = new InternalSink();

        string runSettingsXml = GetRunSettingsXml(string.Empty);
        var context = new InternalDiscoveryContext(runSettingsXml, testCaseFilter);

        unitTestDiscoverer.DiscoverTestsInSource(assemblyPath, logger, sink, context);

        return sink.DiscoveredTests;
    }

    internal static async Task<ImmutableArray<TestResult>> RunTestsAsync(IEnumerable<TestCase> testCases)
    {
        var testExecutionManager = new TestExecutionManager();
        var frameworkHandle = new InternalFrameworkHandle();

        await testExecutionManager.ExecuteTestsAsync(testCases, null, frameworkHandle, false);
        return frameworkHandle.GetFlattenedTestResults();
    }

    #region Helper classes
    private class InternalLogger : IMessageLogger
    {
        public void SendMessage(TestMessageLevel testMessageLevel, string message) => Debug.WriteLine($"{testMessageLevel}: {message}");
    }

    private sealed class InternalSink : ITestCaseDiscoverySink
    {
        private readonly List<TestCase> _testCases = [];

        public ImmutableArray<TestCase> DiscoveredTests => [.. _testCases];

        public void SendTestCase(TestCase discoveredTest) => _testCases.Add(discoveredTest);
    }

    private class InternalDiscoveryContext : IDiscoveryContext
    {
        private readonly ITestCaseFilterExpression? _filter;

        public InternalDiscoveryContext(string runSettings, string? testCaseFilter)
        {
            RunSettings = new InternalRunSettings(runSettings);

            if (testCaseFilter != null)
            {
                _filter = TestCaseFilterFactory.ParseTestFilter(testCaseFilter);
            }
        }

        public IRunSettings? RunSettings { get; }

        public ITestCaseFilterExpression? GetTestCaseFilter(IEnumerable<string> supportedProperties, Func<string, TestProperty> propertyProvider) => _filter;

        private class InternalRunSettings : IRunSettings
        {
            public InternalRunSettings(string runSettings) => SettingsXml = runSettings;

            public string SettingsXml { get; }

            public ISettingsProvider? GetSettings(string? settingsName) => throw new NotImplementedException();
        }
    }

    private sealed class InternalFrameworkHandle : IFrameworkHandle
    {
        private readonly List<string> _messageList = [];
        private readonly ConcurrentDictionary<TestCase, ConcurrentBag<TestResult>> _testResults = new();

        private ConcurrentBag<TestResult> _activeResults = [];

        public bool EnableShutdownAfterTestRun { get; set; }

        public void RecordStart(TestCase testCase) => _activeResults = _testResults.GetOrAdd(testCase, _ => []);

        public void RecordEnd(TestCase testCase, TestOutcome outcome) => _activeResults = _testResults[testCase];

        public void RecordResult(TestResult testResult)
        {
            Assert.IsNotNull(testResult, "Test result should not be null.");
            _activeResults.Add(testResult);
        }

        public ImmutableArray<TestResult> GetFlattenedTestResults()
        {
            var allTestResults = _testResults.SelectMany(i => i.Value).ToImmutableArray();
            CollectionAssert.AllItemsAreNotNull(allTestResults, "All test results should be non-null.");

            return allTestResults;
        }

        public void RecordAttachments(IList<AttachmentSet> attachmentSets)
            => throw new NotImplementedException();

        public void SendMessage(TestMessageLevel testMessageLevel, string message) => _messageList.Add($"{testMessageLevel}:{message}");

        public int LaunchProcessWithDebuggerAttached(string filePath, string? workingDirectory, string? arguments, IDictionary<string, string?>? environmentVariables)
            => throw new NotImplementedException();
    }
    #endregion
}
